#include "plugin.h"
#include <stdio.h>
#include <stack>

struct AlgConnSimple : public AlgConn {
	double radius;
	double step;

	void setParams(const std::vector<ParamValue>& params) override {
		radius=params[0].getFloat();
		step=params[1].getFloat();
	}
	std::vector<ParamValue> getParams() const override {
		std::vector<ParamValue> vl;
		vl.push_back(ParamValue{radius});
		vl.push_back(ParamValue{step});
		return vl;
	}
	AlgConnSimple() : AlgConn(), radius{1.0}, step{1.0} { }

	void connect(const Position& p0, const Position& p1, Graph gr, Path* pth) override {
		auto totDist=p0.point.distTo(p1.point);
		auto n=lrint(totDist/step);
		//auto ss=totDist/(n+1);

		pth->edge0=p0.edge;
		pth->index0=p0.index;
		pth->edge1=p1.edge;
		pth->index1=p1.index;
		pth->points.clear();
		pth->points.push_back({p0.point.x(), p0.point.y(), p0.point.z(), radius});

		auto dx=(p1.point.x()-p0.point.x())/(n+1);
		auto dy=(p1.point.y()-p0.point.y())/(n+1);
		auto dz=(p1.point.z()-p0.point.z())/(n+1);
		for(long int i=0; i<n; i++) {
			auto x=p0.point.x()+(i+1)*dx;
			auto y=p0.point.y()+(i+1)*dy;
			auto z=p0.point.z()+(i+1)*dz;
			pth->points.push_back(Point{x, y, z, radius});
		}
		pth->points.push_back({p1.point.x(), p1.point.y(), p1.point.z(), radius});
	}
	static AlgConn* creater() {
		return new AlgConnSimple{};
	}
};

struct AlgRefineSimple : public AlgRefine {
	static AlgRefine* creater() {
		return new AlgRefineSimple{};
	}
	int mark;
	double radius;
	void setParams(const std::vector<ParamValue>& params) override {
		mark=params[0].getInt();
		radius=params[1].getFloat();
	}
	std::vector<ParamValue> getParams() const override {
		std::vector<ParamValue> vl;
		vl.push_back(ParamValue{mark});
		vl.push_back(ParamValue{radius});
		return vl;
	}
	AlgRefineSimple() : AlgRefine(), mark{0}, radius{.5} { }
	void refine(Graph gr, Path* pth) override {
		size_t a=0;
		size_t b=pth->points.size()-1;
		if(pth->edge0) {
			pth->points[a].m=pth->edge0.points()[pth->index0].m;
			pth->points[a].r(pth->edge0.points()[pth->index0].r());
			a++;
		}
		if(pth->edge1) {
			pth->points[b].m=pth->edge1.points()[pth->index1].m;
			pth->points[b].r(pth->edge1.points()[pth->index1].r());
			b--;
		}
		for(size_t i=a; i<=b; i++) {
			pth->points[i].m=mark;
			pth->points[i].r(radius);
		}
	}
};

static void simple_load(std::vector<AlgorithmInfo*>& algs, std::vector<GraphReaderInfo*>& readers) {
	auto infocon=new AlgInfo<AlgConn>{"Simple", 10,
		"This connect algorithm directly connects two points.",
		{
			ParamSpec{"Radius", "Radius of the nodes.", 0.0, 255.0, .5, "µm"},
			ParamSpec{"Step", "Distance between nodes", 0.1, 99.0, 1.0, "µm"}
		},
		AlgConnSimple::creater};
	auto inforefine=new AlgInfo<AlgRefine>{"Simple", 10,
		"Set mark and radius to a fixed value.",
		{
			ParamSpec{"Mark", "Mark of the nodes.", 0, 255, 0, ""},
			ParamSpec{"Radius", "Radius of the nodes.", 0.0, 255.0, .5, "µm"}
		},
		AlgRefineSimple::creater};
	algs.push_back(infocon);
	algs.push_back(inforefine);
}

static void simple_unload() {
}
extern "C"
{
	PLUGIN_EXPORT
		FNT_PLUGIN("Simple", &simple_load, &simple_unload, "Trivial methods.");
}

